package com.smsc.headend.task.engine.handler.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.smsc.headend.common.enums.errorcode.TaskHandlerErrorCode;
import com.smsc.headend.common.utils.CosemUtils;
import com.smsc.headend.module.asset.entity.Dcu;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.data.dto.AnsiSubDataItemDTO;
import com.smsc.headend.module.data.dto.DataItemDTO;
import com.smsc.headend.module.data.dto.TaskDataDTO;
import com.smsc.headend.module.data.dto.TaskPacketDTO;
import com.smsc.headend.module.data.entity.MapDdsMdmWf;
import com.smsc.headend.module.data.entity.ModuleUnit;
import com.smsc.headend.module.data.enums.OperationGroup;
import com.smsc.headend.module.protocol.DeviceData;
import com.smsc.headend.module.protocol.ansi.AnsiByteBuffer;
import com.smsc.headend.module.protocol.ansi.AnsiData;
import com.smsc.headend.module.protocol.cosem.bo.CosemData;
import com.smsc.headend.module.protocol.cosem.enums.ActionResult;
import com.smsc.headend.module.protocol.cosem.enums.ActionResultDiff;
import com.smsc.headend.module.report.entity.LogScheduleTaskDevice;
import com.smsc.headend.module.task.consts.TaskKafkaTopic;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.task.dto.TaskResult;
import com.smsc.headend.module.task.enums.DeviceActionType;
import com.smsc.headend.module.task.enums.TaskType;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskHandlerException;
import com.smsc.headend.task.engine.facade.DataItemFacade;
import com.smsc.headend.task.engine.handler.TaskHandler;
import com.smsc.headend.task.engine.handler.impl.collection.builder.PhaseRecognitionResultHandler;
import com.smsc.headend.task.engine.handler.impl.collection.builder.ScheduleReadEnum;
import com.smsc.headend.task.engine.mapper.ModuleUnitMapper;
import com.smsc.headend.task.engine.service.CosemXmlService;
import com.smsc.headend.task.engine.service.RdVirtualProfileService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.task.LogScheduleTaskDeviceService;
import com.smsc.headend.task.engine.service.task.impl.TaskServiceImpl;
import com.smsc.headend.task.engine.utils.AnsiUtil;
import com.smsc.headend.task.engine.utils.DdsXmlGenerateUtil;
import gurux.dlms.enums.DataType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Component
public class ReadRegisterValueHandler implements TaskHandler {
    @Autowired
    TaskDAO taskDAO;
    @Autowired
    CosemXmlService cosemXmlService;
    @Autowired
    TaskServiceImpl taskService;
    @Autowired
    KafkaTemplate kafkaTemplate;
    @Autowired
    AssetService assetService;
    @Autowired
    ModuleUnitMapper moduleUnitMapper;
    @Autowired
    RdVirtualProfileService rdVirtualProfileService;
    @Autowired
    LogScheduleTaskDeviceService logScheduleTaskDeviceService;
    @Autowired
    DataItemFacade dataItemFacade;
    @Autowired
    DdsXmlGenerateUtil ddsXmlGenerateUtil;
    @Autowired
    PhaseRecognitionResultHandler phaseRecognitionResultHandler;

    @Override
    public int handle(Task task, AtomicTask atomicTask, DeviceData deviceData, String actionResult) throws UDISTaskHandlerException {
        if (!(deviceData instanceof CosemData) && !(deviceData instanceof AnsiData)) {
            throw new UDISTaskHandlerException(TaskHandlerErrorCode.DEVICE_DATA_TYPE_NOT_SUPPORT.toString());
        }
        Long tv = SystemClock.now() / 1000;
        Dcu dcu = null;
        Meter meter = assetService.getMeterById(task.getMeterId());
        Map<String, Object> taskParam = JSONUtil.toBean(task.getParamJson(), Map.class);
        Map<String, Object> m = JSONUtil.toBean(atomicTask.getAtomicTaskParam(), Map.class);
        if (taskParam.containsKey("dcuId")) {
            Long dcuId = Long.valueOf(String.valueOf(taskParam.get("dcuId")));
            dcu = assetService.getDcuById(dcuId);
        }
        Long subDataItemId = Long.valueOf(String.valueOf(m.get("dataItemId")));
        Object data;
        if (deviceData instanceof AnsiData) {
            AnsiData ansiData = (AnsiData) deviceData;
            data = getAnsiData(ansiData, meter, subDataItemId, task.getTaskType());
            log.debug("ReadRegisterValueHandler ansiData={},data={},subDataItemId={}", HexUtil.format(HexUtil.encodeHexStr(ansiData.getAnsiByteBuffer().getData())), data, subDataItemId);
        } else {
            //相位不入库
            if (ScheduleReadEnum.PHASE_RECOGNITION.getDataItemId().equals(subDataItemId)) {
                data = "";
//                String consistent = assetService.getSystemProperty("CONSISTENT_DEVICE_PROTOCOL_ENABLE");
//                List<MeterPhaseDTO> meterPhaseDTOListFromMeter = new ArrayList<>();
//                if (org.apache.commons.lang.StringUtils.isEmpty(consistent) || Boolean.valueOf(consistent).equals(Boolean.FALSE)) {
//                    phaseRecognitionResultHandler.translatePhaseOld(task, deviceData, new ArrayList<>(), meterPhaseDTOListFromMeter);
//                } else {
//                    phaseRecognitionResultHandler.translatePhase(task, deviceData, new ArrayList<>(), meterPhaseDTOListFromMeter);
//                }
//                data = meterPhaseDTOListFromMeter.stream().map(e -> e.getMfgSN() + "[" + e.getPhase() + "]").collect(Collectors.joining(","));
            } else {
                CosemData cosemData = (CosemData) deviceData;
                data = getCosemData(cosemData, meter, subDataItemId, task.getTaskType());
                log.debug("ReadRegisterValueHandler cosemData={},data={},subDataItemId={}", JSONUtil.toJsonStr(cosemData), data, subDataItemId);
            }
        }
        if (task.getIsODR()) {
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), String.valueOf(m.get("dataItemId")), data, Long.valueOf(2 * 60 * 60), true);
        }
        if (TaskType.Interface_ODR_Data.name().equals(task.getTaskType()) || TaskType.ESI_ODR_READ.name().equals(task.getTaskType())) {
            log.info("Interface_ODR_Data result successfully returned, task is: {}, result is:{}",
                    JSONUtil.toJsonStr(task), JSONUtil.toJsonStr(deviceData)
            );
            TaskResult taskResult = new TaskResult();
            taskResult.setTaskNo(task.getTaskNo());
            taskResult.setResultData(data);
            Map<String, Object> dataItemMap = JSONUtil.toBean(atomicTask.getAtomicTaskParam(), Map.class);
            taskParam.putAll(dataItemMap);
            if (null != meter) {
                taskParam.put("meterModelId", meter.getModelId());
            }
            if (null != dcu) {
                taskParam.put("dcuModelId", dcu.getModelId());
            }
            taskParam.putAll(dataItemMap);
            String messageId = null == taskParam.get("externalMessageId") ? task.getMessageId() : String.valueOf(taskParam.get("externalMessageId"));
            taskResult.setMessageId(messageId);
            List<TaskPacketDTO> taskPacketDTOList = taskService.getTaskPacket(task.getTaskNo());
            if (CollectionUtil.isNotEmpty(taskPacketDTOList)) {
                log.info("Interface_ODR_Data packet result are: {}", JSONUtil.toJsonStr(taskPacketDTOList));

                if (CollectionUtil.isNotEmpty(taskPacketDTOList)) {
                    taskParam.put("packets", taskPacketDTOList);
                } else {
                    log.error("odr data item reply without packets! taskNo: {}", task.getTaskNo());
                }
            }

            taskResult.setParamJson(JSONUtil.toJsonStr(taskParam));
            kafkaTemplate.send(TaskKafkaTopic.TASK_COMPLETE_RESULT_FROM_UTE_TO_ADAPTER, JSONUtil.toJsonStr(taskResult).getBytes(StandardCharsets.UTF_8));
        }
        if (TaskType.Read_With_List.name().equals(task.getTaskType())) {
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), String.valueOf(m.get("dataItemId")), data, Long.valueOf(2 * 60 * 60), true);
            if (isSubTaskGroupLastAtomicTask(task, atomicTask)) {
                List<TaskDataDTO> taskDataDTOS = taskDAO.getAllTaskData(task.getTaskNo());
                TaskResult taskResult = new TaskResult();
                taskResult.setTaskNo(task.getTaskNo());
                taskResult.setResultData(taskDataDTOS);
                taskResult.setMessageId(task.getMessageId());
                kafkaTemplate.send(TaskKafkaTopic.TASK_COMPLETE_RESULT_FROM_UTE_TO_ADAPTER, JSONUtil.toJsonStr(taskResult).getBytes(StandardCharsets.UTF_8));
            }
        }
        if (TaskType.ScheduleRead_Virtual_Profile.name().equals(task.getTaskType()) || TaskType.UAA_MeterReads.name().equals(task.getTaskType()) || TaskType.ODR_Virtual_Profile.name().equals(task.getTaskType())) {
            Map<String, Object> params = getParameterMap(task, atomicTask);
            String dataItemId = String.valueOf(m.get("dataItemId"));
            //防止ODR抄读重复缓存
            if (!TaskType.ODR_Virtual_Profile.name().equals(task.getTaskType())) {
                taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), dataItemId, data, Long.valueOf(2 * 60 * 60), true);
            }
            if (TaskType.ScheduleRead_Virtual_Profile.name().equals(task.getTaskType())) {
                logScheduleTaskDeviceService.sendMessage(task, LogScheduleTaskDevice.StatusEnum.Success.getCode(), LogScheduleTaskDevice.StatusDescEnum.CollectedSuccess.getCode());
            }
            if (!ScheduleReadEnum.PHASE_RECOGNITION.getDataItemId().equals(subDataItemId)) {
                //相位不入库
                rdVirtualProfileService.saveRdRecord(task, atomicTask, data);
            }
            if (isSubTaskGroupLastAtomicTask(task, atomicTask)) {
                if (TaskType.UAA_MeterReads.name().equals(task.getTaskType())) {
                    params.put("meterId", task.getMeterId());
                    params.put("taskType", task.getTaskType());
                    params.put("tv", tv);
                    List<TaskDataDTO> taskDataDTOS = taskDAO.getAllTaskData(task.getTaskNo());
                    TaskResult taskResult = new TaskResult();
                    taskResult.setTaskNo(task.getTaskNo());
                    taskResult.setResultData(taskDataDTOS);
                    taskResult.setMessageId(task.getMessageId());
                    params.put("meterModelId", meter.getModelId());
                    params.put("meterCustomSn", meter.getCustomerSn());
                    taskResult.setParamJson(JSONUtil.toJsonStr(params));
                    rdVirtualProfileService.rdDataTransfer(taskResult);
                }
            }
        }

        return 1;
    }

    @Override
    public int handleFailure(Task task, AtomicTask atomicTask, String message) {
        log.info("error {}, ", message);
        int res = -1;
        String key = "message";
        if (atomicTask.getGroupId() == OperationGroup.ReadRegister.getId().intValue() && !TaskType.UAA_MeterReads.name().equals(task.getTaskType())) {
            Map<String, Object> m = JSONUtil.toBean(atomicTask.getAtomicTaskParam(), Map.class);
            key = m.get("dataItemId") != null ? Convert.toStr(m.get("dataItemId")) : key;
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), key, message, 3600L, false);
        }

        // 返回给MDM 随抄数据项接口失败的结果
        Map<String, ActionResult> actionResultMap = EnumUtil.getEnumMap(ActionResult.class);
        Map<String, ActionResultDiff> actionResultDiffMap = EnumUtil.getEnumMap(ActionResultDiff.class);
        if (TaskType.Interface_ODR_Data.name().equals(task.getTaskType()) || TaskType.ESI_ODR_READ.name().equals(task.getTaskType()) || TaskType.Read_With_List.name().equals(task.getTaskType()) || TaskType.ODR_Virtual_Profile.name().equals(task.getTaskType())) {
            TaskResult taskResult = new TaskResult();
            taskResult.setTaskNo(task.getTaskNo());
            Map<String, Object> dataItemMap = JSONUtil.toBean(atomicTask.getAtomicTaskParam(), Map.class);
            Map<String, Object> taskMap = JSONUtil.toBean(task.getParamJson(), Map.class);
            Meter meter = assetService.getMeterById(task.getMeterId());
            taskMap.put("meterModelId", meter.getModelId());
            List<TaskPacketDTO> taskPacketDTOList = taskService.getTaskPacket(task.getTaskNo());
            if (CollectionUtil.isNotEmpty(taskPacketDTOList)) {
                log.info("Interface_ODR_Data packet result are: {}", JSONUtil.toJsonStr(taskPacketDTOList));

                if (CollectionUtil.isNotEmpty(taskPacketDTOList)) {
                    taskMap.put("packets", taskPacketDTOList);
                } else {
                    log.error("odr data item reply without packets! taskNo: {}", task.getTaskNo());
                }
            }
            taskMap.putAll(dataItemMap);
            String messageId = null == taskMap.get("externalMessageId") ? task.getMessageId() : String.valueOf(taskMap.get("externalMessageId"));
            taskResult.setMessageId(messageId);
            taskResult.setExceptionCause(message);
            taskResult.setParamJson(JSONUtil.toJsonStr(taskMap));
            kafkaTemplate.send(TaskKafkaTopic.TASK_COMPLETE_RESULT_FROM_UTE_TO_ADAPTER, JSONUtil.toJsonStr(taskResult).getBytes(StandardCharsets.UTF_8));
        }
        if (TaskType.UAA_MeterReads.name().equals(task.getTaskType())) {
            if (actionResultMap.containsKey(message) || actionResultDiffMap.containsKey(message)) {
                res = 1;
            }
//                Long dataItemId = (Long) taskMap.get("dataItemId");
//                MapDdsMdmWf mapDdsMdmWf = ddsXmlGenerateUtil.getMapDdsMdmWf(9999999L, dataItemId);
//                message = (mapDdsMdmWf != null && !StringUtils.isEmpty(mapDdsMdmWf.getMdmDataItemId()) ? mapDdsMdmWf.getMdmDataItemId() : dataItemId) + ":" + message;
            if (isSubTaskGroupLastAtomicTask(task, atomicTask)) {
                Map<String, Object> params = getParameterMap(task, atomicTask);
                Meter meter = assetService.getMeterById(task.getMeterId());
                Long tv = SystemClock.now() / 1000;
                params.put("meterId", task.getMeterId());
                params.put("taskType", task.getTaskType());
                params.put("tv", tv);
                List<TaskDataDTO> taskDataDTOS = taskDAO.getAllTaskData(task.getTaskNo());
                TaskResult taskResult = new TaskResult();
                taskResult.setTaskNo(task.getTaskNo());
                taskResult.setResultData(taskDataDTOS);
                taskResult.setMessageId(task.getMessageId());
                params.put("meterModelId", meter.getModelId());
                params.put("meterCustomSn", meter.getCustomerSn());
                taskResult.setParamJson(JSONUtil.toJsonStr(params));
                rdVirtualProfileService.rdDataTransfer(taskResult);
            }
        }

        if (StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.ScheduleRead_Virtual_Profile.name())) {
            if (!actionResultMap.containsKey(message) || !actionResultDiffMap.containsKey(message)) {
                logScheduleTaskDeviceService.sendMessage(task, LogScheduleTaskDevice.StatusEnum.Failed.getCode(), LogScheduleTaskDevice.StatusDescEnum.DecodeDataFailed.getCode());
            } else {
                logScheduleTaskDeviceService.sendMessage(task, LogScheduleTaskDevice.StatusEnum.Success.getCode(), LogScheduleTaskDevice.StatusDescEnum.CollectedSuccess.getCode());
            }
        }
        return res;
    }

    /**
     * 根据MAP_MODULE_UNIT的配置，判断是否需要对数据进行转换
     *
     * @param cosemData
     * @param meter
     * @param subDataItemId
     * @param taskType
     * @return
     */
    public Object getCosemData(CosemData cosemData, Meter meter, Long subDataItemId, String taskType) {
        Object data = cosemXmlService.getData(cosemData.getType(), cosemData.getValue());
        if (DataType.OCTET_STRING.toString().equalsIgnoreCase(cosemData.getType())) {
            if (CosemUtils.isDateTimeString(Convert.toStr(data))) {
                data = DateFormatUtils.format(CosemUtils.parseOctStringDataTime(Convert.toStr(data)), "yyyy-MM-dd HH:mm:ss");
            } else if (CosemUtils.isLocalDate(Convert.toStr(data))) {
                data = CosemUtils.parseOctStringDate(Convert.toStr(data));
            } else if (CosemUtils.isLocalTime(Convert.toStr(data))) {
                data = CosemUtils.parseOctStringTime(Convert.toStr(data));
            } else if (CosemUtils.isAsciiHexStr(Convert.toStr(data))) {
                // 无量纲配置，默认转换
                ModuleUnit moduleUnit = null;
                String moduleName = TaskType.Interface_ODR_Data.name().equals(taskType) ? "MDM" : "GUI";
                if (StrUtil.equalsIgnoreCase("GUI", moduleName)) {
                    // 返回给GUI原值,GUI本身会调用asset-management做再一次转换
                    return data;
                }
                if (null != meter) {
                    moduleUnit = moduleUnitMapper.getBySubDataItemIdAndModelId(subDataItemId, meter.getModelId(), moduleName);
                    if (null == moduleUnit) {
                        moduleUnit = moduleUnitMapper.getBySubDataItemIdAndModelId(subDataItemId, 0L, moduleName);
                    }
                }
                if (moduleUnit != null && moduleUnit.getIsConvert() != null && moduleUnit.getIsConvert() == 1) {
                    data = HexUtil.decodeHexStr(Convert.toStr(data));
                }
            }
        }
        return data;
    }

    /**
     * 根据MAP_MODULE_UNIT的配置，判断是否需要对数据进行转换
     *
     * @param ansiData
     * @param meter
     * @param subDataItemId
     * @param taskType
     * @return
     */
    public Object getAnsiData(AnsiData ansiData, Meter meter, Long subDataItemId, String taskType) {
        Object data = null;
        List<DataItemDTO> dataItems = dataItemFacade.getDataItemsById(meter.getModelId(), DeviceActionType.Get, subDataItemId);
        AnsiSubDataItemDTO ansiSubDataItem = (AnsiSubDataItemDTO) dataItems.get(0);
        if ("DATE_TIME".equals(ansiSubDataItem.getDataType())) {
            data = AnsiUtil.parseDateTimeString(ansiData.getAnsiByteBuffer());
        } else if ("INT32".equals(ansiSubDataItem.getDataType())) {
            AnsiByteBuffer ansiByteBuffer = ansiData.getAnsiByteBuffer();
            ansiByteBuffer.getUInt8();
            int byteSize = ansiByteBuffer.getUInt16();
            if(byteSize == 4) {
                data = AnsiUtil.parseValue4Byte(ansiByteBuffer, false);
            } else if (byteSize == 2) {
                data = AnsiUtil.parseValue2Byte(ansiByteBuffer, false);
            }
        } else if ("STRING".equals(ansiSubDataItem.getDataType())) {
            data = AnsiUtil.parseValueString(ansiData.getAnsiByteBuffer());
        }

        return data;
    }

}

